﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Media.Animation;
using Kinect.Framework.Lib;
using Kinect.Framework.Interfaces;
using ManagedNite;
//using Kinect.Framework.AttachProperties;

namespace Kinect.Framework.Controls
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Kinect.Controls"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Kinect.Controls;assembly=Kinect.Controls"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Browse to and select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:KinectPointer/>
    ///
    /// </summary>
    public class KinectPointer : Control
    {
        #region Events SelectionRaised
        public delegate void SelectionRaisedEventHandler(object s, PayloadEventArgs<UIElement> e);
        public event SelectionRaisedEventHandler SelectionRaised;
        public void OnSelectionRaised(UIElement value)
        {
            if (SelectionRaised != null)
                SelectionRaised(this, new PayloadEventArgs<UIElement>(value));
        }
        #endregion

        public uint Id { get; set; }
        public XnMPoint Position { get; set; }
        public XnMPoint RealPosition { get; set; }
        public UIElement CurrentSelectedElement { get; set; }
        public XnMPoint LastPoint { get; set; }
        public XnMPoint Offset { get; set; }
        private Storyboard _currentStoryboard;

        private DispatcherTimer timer;

        static KinectPointer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(KinectPointer), new FrameworkPropertyMetadata(typeof(KinectPointer)));

        }
        public KinectPointer()
        {
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(2);
            timer.Tick += new EventHandler(OnTick);
        }

        void OnTick(object sender, EventArgs e)
        {
            timer.Stop();
            RoutedEventArgs ClickEventArgs = new RoutedEventArgs(KinectManipulation.ClickEvent);
            CurrentSelectedElement.RaiseEvent(ClickEventArgs);
        }
        public void StartSelection()
        {
            VisualStateManager.GoToState(this, "Loading", false);
            timer.Start();
        }
        public void StopSelection()
        {
            VisualStateManager.GoToState(this, "StopLoading", false);
            timer.Stop();
        }

        public void Move(XnMPoint pos, XnMPoint realPos)
        {
            Position = pos;
            RealPosition = realPos;

            if (_currentStoryboard != null)
                return;
            _currentStoryboard = new Storyboard();

            int total = 0;
            DoubleAnimation oLeftMoveAnimation = new DoubleAnimation();
            oLeftMoveAnimation.Completed += (se, ev) =>
            {
                total++;
                if (total == 2)
                    _currentStoryboard = null;
            };
            oLeftMoveAnimation.DecelerationRatio = 0.7;
            oLeftMoveAnimation.From = Position.X;
            oLeftMoveAnimation.To = pos.X;
            oLeftMoveAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(25));
            BeginAnimation(Canvas.LeftProperty, oLeftMoveAnimation);

            DoubleAnimation oTopMoveAnimation = new DoubleAnimation();
            oTopMoveAnimation.Completed += (se, ev) =>
            {
                total++;
                if (total == 2)
                    _currentStoryboard = null;
            };
            oTopMoveAnimation.DecelerationRatio = 0.7;
            oTopMoveAnimation.From = Position.Y;
            oTopMoveAnimation.To = pos.Y;
            oTopMoveAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(25));
            BeginAnimation(Canvas.TopProperty, oTopMoveAnimation);
        }

        public HitTestFilterBehavior IsHitTestVisibleFilter(DependencyObject o)
        {
            if (((UIElement)o).IsHitTestVisible == false)
                return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
            else
                return HitTestFilterBehavior.Continue;
        }
        List<DependencyObject> hitResultsList = new List<DependencyObject>();
        public HitTestResultBehavior HitTestResult(HitTestResult result)
        {
            hitResultsList.Add(result.VisualHit);
            return HitTestResultBehavior.Stop;
        }

        public void UpdateManipulatedElement(Visual context)
        {
            if (Position == null)
                return;
            List<UIElement> KinectElements = VisualTreeHelpers.FindVisualChildsImplentingIKinectElement<UIElement>(context) as List<UIElement>;

            bool ElementsFounded = false;
            UIElement SelectedElement = null;

            hitResultsList.Clear();

            VisualTreeHelper.HitTest(context,
                              new HitTestFilterCallback(IsHitTestVisibleFilter),
                              new HitTestResultCallback(HitTestResult),
                              new PointHitTestParameters(new Point() { X = Position.X + (this.ActualWidth / 2), Y = Position.Y + (this.ActualHeight / 2) }));

            if (hitResultsList.Count > 0)
            {
                var elem = VisualTreeHelpers.FindVisualParentWithAttachedProperty<UIElement>(hitResultsList[0]);
                if (elem != null)
                {


                    if (KinectManipulation.GetIsSelectable((UIElement)elem))
                    {
                        ElementsFounded = true;
                        this.StartSelection();
                    }
                    ((IKinectElement)elem).GotKinectFocus(Position, RealPosition, (UIElement)context);
                    SelectedElement = elem;
                    if (KinectElements != null)
                        KinectElements.Cast<IKinectElement>().Where(UIElement => UIElement != elem).ToList().ForEach(UIElement => UIElement.LostKinectFocus());
                }
            }
            if (!ElementsFounded)
            {
                SelectedElement = null;
                this.StopSelection();
            }
            if (SelectedElement != null)
                KinectWindow.GestureController(this).HasPointerSelectedElement = true;
            else
                KinectWindow.GestureController(this).HasPointerSelectedElement = false;
            if (KinectElements != null)
                KinectElements.Cast<IKinectElement>().Where(UIElement => UIElement != SelectedElement).ToList().ForEach(UIElement => UIElement.LostKinectFocus());
            CurrentSelectedElement = SelectedElement;
        }
    }
}
